package handler

import (
	"errors"
	"gitee.com/eqyun/gosdk/config"
	"gitee.com/eqyun/gosdk/sdk"
	"gitee.com/eqyun/gosdk/sdk/common"
	"gitee.com/eqyun/gosdk/sdk/common/global"
	"gitee.com/eqyun/gosdk/sdk/pkg"
	"gitee.com/eqyun/gosdk/sdk/pkg/captcha"
	jwt "github.com/appleboy/gin-jwt/v2"
	"github.com/gin-gonic/gin"
	"github.com/mssola/user_agent"
	"net/http"
	"time"
)

func IdentityHandler(c *gin.Context) interface{} {
	claims := jwt.ExtractClaims(c)
	return LoginUser{
		IdAuto: int(claims[jwt.IdentityKey].(float64)),
		Type:   claims["user_type"].(string),
		Name:   claims["user_name"].(string),
	}
}
func PayloadFunc(data interface{}) jwt.MapClaims {
	if v, ok := data.(LoginUser); ok {
		return jwt.MapClaims{
			jwt.IdentityKey: v.IdAuto,
			"user_type":     v.Type,
			"user_name":     v.Name,
			//jwt.RoleNameKey:  r.RoleName,
		}
		/*u, _ := v["user"].(SysUser)
		r, _ := v["role"].(SysRole)
		return jwt.MapClaims{
			jwt.IdentityKey:  u.UserId,
			jwt.RoleIdKey:    r.RoleId,
			jwt.RoleKey:      r.RoleKey,
			jwt.NiceKey:      u.Username,
			jwt.DataScopeKey: r.DataScope,
			jwt.RoleNameKey:  r.RoleName,
		}*/
	}
	return jwt.MapClaims{}
}

// Authenticator 获取token
// @Summary 登陆
// @Description 获取token
// @Description LoginHandler can be used by clients to get a jwt token.
// @Description Payload needs to be json in the form of {"username": "USERNAME", "password": "PASSWORD"}.
// @Description Reply will be of the form {"token": "TOKEN"}.
// @Description dev mode：It should be noted that all fields cannot be empty, and a value of 0 can be passed in addition to the account password
// @Description 注意：开发模式：需要注意全部字段不能为空，账号密码外可以传入0值
// @Tags 登陆
// @Accept  application/json
// @Product application/json
// @Param account body Login  true "account"
// @Success 200 {string} string "{"code": 200, "expire": "2019-08-07T12:45:48+08:00", "token": ".eyJleHAiOjE1NjUxNTMxNDgsImlkIjoiYWRtaW4iLCJvcmlnX2lhdCI6MTU2NTE0OTU0OH0.-zvzHvbg0A" }"
// @Router /api/v1/login [post]
func Authenticator(c *gin.Context) (interface{}, error) {
	var loginVals Login

	var status = "2"
	var msg = "登录成功"
	var username = ""
	defer func() {
		LoginLogToDB(c, status, msg, username)
	}()
	db, _ := pkg.GetOrm(c)
	if err := c.ShouldBind(&loginVals); err != nil {
		username = loginVals.Username
		msg = "数据解析失败"
		status = "1"
		return "", jwt.ErrMissingLoginValues
	}
	if config.ApplicationConfig.Mode == "prod" {
		if !captcha.Verify(loginVals.UUID, loginVals.Code, true) {
			username = loginVals.Username
			msg = "验证码错误"
			status = "1"
			return nil, errors.New("验证码错误")
		}
	}

	user, e := loginVals.GetUser(db)

	if e == nil || config.ApplicationConfig.Mode != "prod" {
		username = user.Name
		c.Set("userName", username)
		//username
		/*username = loginVals.Username
		return map[string]interface{}{"user": user, "role": role}, nil*/
		return user, nil
	} else {
		msg = "登录失败"
		status = "1"
	}
	//TODO add login log
	return nil, jwt.ErrFailedAuthentication

}

func Authorizator(data interface{}, c *gin.Context) bool {
	token := jwt.GetToken(c)

	if v, ok := data.(LoginUser); ok {
		//if d, h := sdk.Runtime.GetCachePrefix("db_cache").Get(sql); nil == h && "" != d {
		if d, h := sdk.Runtime.GetCachePrefix(global.CacheSingleLogin).Get(v.Name); (nil == h && d == token) || config.ApplicationConfig.Mode != "prod" {
			c.Set("userId", v.IdAuto)
			c.Set("userName", v.Name)
			c.Set("userType", v.Type)
			return true
		}

	}
	return false
}

// GenerateCaptchaHandler 退出登录
// @Summary 退出登录
// @Description 退出登录
// @Tags 登陆
// @Success 200 {object} response.Response{data=string,id=string,msg=string} "{"code": 200, "data": [...]}"
// @Router /api/v1/loginout [post]
func Unauthorized(c *gin.Context, code int, message string) {

	if code == http.StatusForbidden {
		message = "登錄失效，請重新登錄"
	}
	c.JSON(http.StatusOK, gin.H{
		"code": code,
		"msg":  message,
	})
}

func LoginLogToDB(c *gin.Context, status string, msg string, username string) {
	l := make(map[string]interface{})

	ua := user_agent.New(c.Request.UserAgent())
	l["ipaddr"] = common.GetClientIP(c)
	l["loginTime"] = pkg.GetCurrentTime()
	l["status"] = status
	l["remark"] = c.Request.UserAgent()
	browserName, browserVersion := ua.Browser()
	l["browser"] = browserName + " " + browserVersion
	l["os"] = ua.OS()
	l["platform"] = ua.Platform()
	l["username"] = username
	l["msg"] = msg
	location, e := pkg.GetIpLocation(l["ipaddr"].(string))
	if e != nil {
		l["location"] = "unknown"
	} else {
		l["location"] = location
	}
	q := sdk.Runtime.GetMemoryQueue(c.Request.Host)
	message, err := sdk.Runtime.GetStreamMessage("", global.LoginLog, l)
	if err != nil {
		//log.Errorf("GetStreamMessage error, %s", err.Error())
		//日志报错错误，不中断请求
	} else {
		err = q.Append(message)
		if err != nil {
			//log.Errorf("Append message error, %s", err.Error())
		}
	}
}

func LogoutResponse(c *gin.Context, code int) {
	name, _ := c.Get("userName")
	if d, h := sdk.Runtime.GetCachePrefix(global.CacheSingleLogin).Get(name.(string)); nil == h && d == jwt.GetToken(c) {
		sdk.Runtime.GetCachePrefix(global.CacheSingleLogin).Del(name.(string))
	}
	c.JSON(http.StatusOK, gin.H{
		"code": http.StatusOK,
	})
}

func LoginResponse(c *gin.Context, code int, token string, expire time.Time) {

	//userName
	name, _ := c.Get("userName")
	sdk.Runtime.GetCachePrefix(global.CacheSingleLogin).Set(name.(string), token, 0)
	c.JSON(http.StatusOK, gin.H{
		"code":   http.StatusOK,
		"token":  token,
		"expire": expire.Format(time.RFC3339),
	})
}
